using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;

namespace PropertyExpression.ControllableQuery.Toolkit
{
    public delegate TResult Func<T1, T2, T3, T4, T5, T6, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    public delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);

    public static class Util
    {
        public static bool IsGenericTypeAndEquals(this Type type1, Type type2)
        {
            return type1.IsGenericType && type2.Equals(type1.GetGenericTypeDefinition());
        }

        public static bool GetAllowDbNull(this DataRow dataRow)
        {
            return (bool)dataRow["AllowDBNull"];
        }

        public static int GetColumnSize(this DataRow dataRow)
        {
            return (int)dataRow["ColumnSize"];
        }

        public static short GetNumericPrecision(this DataRow dataRow)
        {
            return (short)dataRow["NumericPrecision"];
        }

        public static short GetNumericScale(this DataRow dataRow)
        {
            return (short)dataRow["NumericScale"];
        }

        public static Type GetDataType(this DataRow dataRow)
        {
            return (Type)dataRow["DataType"];
        }

        public static string GetColumnName(this DataRow dataRow)
        {
            return (string)dataRow["ColumnName"];
        }

        public static string GetDataTypeName(this DataRow dataRow)
        {
            return (string)dataRow["DataTypeName"];
        }

        public static bool CompareSqlTypes(this string sqlType1, string sqlType2)
        {
            return String.Compare(sqlType1, sqlType2, true) == 0;
        }

        public static DataTable ListToDataTable(IList list)
        {
            DataTable dt = null;
            if (list.GetType().IsGenericType)
            {
                Type itemType = list.GetType().GetGenericArguments()[0];
                PropertyInfo[] pis = itemType.GetProperties();
                dt = new DataTable();
                System.Data.DataRow dr;
                foreach (System.Reflection.PropertyInfo pInfo in pis)
                {
                    dt.Columns.Add(pInfo.Name, pInfo.PropertyType);
                }
                foreach (object elem in list)
                {
                    dr = dt.NewRow();
                    foreach (System.Reflection.PropertyInfo pInfo in pis)
                    {
                        dr[pInfo.Name] = pInfo.GetValue(elem, null);
                    }
                    dt.Rows.Add(dr);
                }
            }
            return dt;
        }

        public static DataTable IEnumerableToDataTable(IEnumerable list)
        {
            DataTable dt = null;
            if (list.GetType().IsGenericType)
            {
                Type itemType = list.GetType().GetGenericArguments()[0];
                PropertyInfo[] pis = itemType.GetProperties();
                dt = new DataTable();
                System.Data.DataRow dr;
                foreach (System.Reflection.PropertyInfo pInfo in pis)
                {
                    var propertyType = pInfo.PropertyType;
                    Type type;
                    if (propertyType.IsGenericType
                        && typeof(Nullable<>).Equals(propertyType.GetGenericTypeDefinition()))
                    {
                        type = propertyType.GetGenericArguments().Single();
                    }
                    else
                    {
                        type = propertyType;
                    }
                    dt.Columns.Add(pInfo.Name, type);
                }
                foreach (object elem in list)
                {
                    dr = dt.NewRow();
                    foreach (System.Reflection.PropertyInfo pInfo in pis)
                    {
                        var propertyType = pInfo.PropertyType;
                        var propertyValue = pInfo.GetValue(elem, null);
                        object value;
                        if (propertyType.IsGenericType
                            && typeof(Nullable<>).Equals(propertyType.GetGenericTypeDefinition()))
                        {
                            value = getValueForDataRowMethodInfo.MakeGenericMethod(
                                propertyType.GetGenericArguments()).Invoke(null, new[] { propertyValue });
                        }
                        else
                        {
                            value = propertyValue;
                        }
                        dr[pInfo.Name] = value;
                    }
                    dt.Rows.Add(dr);
                }
            }
            return dt;
        }

        private static object GetValueForDataRow<T>(object value) where T : struct
        {
            var nullableValue = (T?)value;
            return !nullableValue.HasValue ? (object) DBNull.Value : nullableValue.Value;
        }

        private static readonly MethodInfo getValueForDataRowMethodInfo =
            QWriter.GetMethodInfo<Func<object, object>>(x => GetValueForDataRow<int>(x)).GetGenericMethodDefinition();
    }
}